#include "PreCompile.hpp"
#include "GObjHelpers.hpp"
#include "Logging.hpp"
#include "Utils.hpp"

namespace BuildNG
{
  //////////
  // ObjectBase
  ObjectBase::ObjectBase()
  {
    ref_count = 1;
    weakptr_stub = 0;
  }

  ObjectBase::~ObjectBase()
  {
  }

  void ObjectBase::reference()
  {
    g_atomic_int_inc(&ref_count);
  }

  void ObjectBase::unreference()
  {
    if(g_atomic_int_dec_and_test(&ref_count))
    {
      if(weakptr_stub)
      {
        weakptr_stub->obj_mutex.lock();
        if(g_atomic_int_get(&ref_count) != 0)
        {
          weakptr_stub->obj_mutex.unlock();
          return;
        }
        weakptr_stub->obj = NULL;
        weakptr_stub->obj_mutex.unlock();

        if(g_atomic_int_dec_and_test(&weakptr_stub->stub_refcount))
          delete weakptr_stub;
      }

      delete this;
    }
  }

  //////////
  // WeakPtrBase
  WeakPtrBase::WeakPtrBase()
  {
    obj_stub = NULL;
  }

  WeakPtrBase::WeakPtrBase(const WeakPtrBase &src)
  {
    obj_stub = src.obj_stub;
    g_atomic_int_inc(&obj_stub->stub_refcount);
  }

  WeakPtrBase::~WeakPtrBase()
  {
    clear();
  }

  void WeakPtrBase::assign(const Glib::RefPtr<ObjectBase> &src)
  {
    clear();

    if(src)
    {
      WeakPtrStub *stub = (WeakPtrStub*)g_atomic_pointer_get((gpointer*)&src->weakptr_stub);

      if(!src->weakptr_stub)
      {
        stub = new WeakPtrStub;
        stub->obj = src.operator->();    // those glibmm people need to provide a nomrla way for doing this. this stuff is just rediculous.

        if(!g_atomic_pointer_compare_and_exchange((gpointer*)&src->weakptr_stub,NULL,stub))
          delete stub;

        stub = (WeakPtrStub*)g_atomic_pointer_get((gpointer*)&src->weakptr_stub);
      }
      
      g_atomic_int_inc(&stub->stub_refcount);

      obj_stub = stub;
    }
  }

  void WeakPtrBase::assign(const WeakPtrBase &src)
  {
    clear();

    if(src.obj_stub)
    {
      obj_stub = src.obj_stub;
      g_atomic_int_inc(&obj_stub->stub_refcount);
    }
  }

  void WeakPtrBase::clear()
  {
    if(obj_stub)
    {
      if(g_atomic_int_dec_and_test(&obj_stub->stub_refcount))
      {
        delete obj_stub;
      }
      obj_stub = NULL;
    }
  }

  Glib::RefPtr<ObjectBase> WeakPtrBase::acquire()
  {
    if(obj_stub)
    {
      Glib::RefPtr<ObjectBase> res;

      obj_stub->obj_mutex.lock();
      if(obj_stub->obj)
      {
        obj_stub->obj->reference();
        res = Glib::RefPtr<ObjectBase>(obj_stub->obj);
      }
      obj_stub->obj_mutex.unlock();

      return res;
    }
    else
      return Glib::RefPtr<ObjectBase>();
  }
}
